#ifndef	__DBOCI_H__
#define __DBOCI_H__

#ifdef __ORACLE__
#include "RtBase.h"
#include "RtDefines.h"
#include <oci.h>

/*-----------------------------------------------------*/

typedef unsigned short USHORT;

#ifndef	ENUM_DATA_TYPE
#define	ENUM_DATA_TYPE
typedef enum {
		_DataType_Null =		0,
		_DataType_Bool =		1,
		_DataType_UChar,
		_DataType_Short,
		_DataType_Long,
		_DataType_Int64,
		_DataType_Float,
		_DataType_Double,
		_DataType_Time,
		_DataType_String,
		_DataType_Binary,
} _DataType;
#endif

/* for use to call CDatabase_::GetItemValue() */
#define DTY_BOOL                1
#define DTY_BYTE                2
#define DTY_SHORT               3
#define DTY_LONG                4
#define DTY_LONGLONG			5
#define DTY_FLOAT               6
#define DTY_DOUBLE              7
#define DTY_TIME				8
#define DTY_STRING				9
#define DTY_BINARY				10
#define DTY_WORD                11
#define DTY_DWORD               12
#define DTY_NUMBER				13
#define DTY_PDN					14
/*-----------------------------------------------------*/
//for community :	Greenwich Mean Time
#ifndef	STRUCT_GMT_TIME
#define STRUCT_GMT_TIME
typedef struct {
		WORD		wYear;
		WORD		wMonth;
		WORD		wDay;
		WORD		wHour;
		WORD		wMinute;
		WORD		wSecond;
		WORD		wReserved;
}  gmt_time;
#endif

/*-----------------------------------------------------*/
#define _MAX_DBLENGTH           256
#define _MAX_SQLLENGTH          16384

#ifndef _MAX_SQL_LENGTH
#define _MAX_SQL_LENGTH			10240
#endif

#define _MAX_PARAMETERS         32
#define _MAX_ERRMESSAGE		1024

#define MEM_ALLOC_INCREMENT     16
#define DELTA                   16

#define IS_PROCEDURE            0x01
#define IS_FUNCTION             0x02
#define IS_STATEMENT            0x04
#define IS_CURSOR               0x08

#define P_CURSOR                ":P_CURSOR"
#define P_RETURN                ":P_RETURN"
/*-----------------------------------------------------*/

/*-----------------------------------------------------*/
#define DB_SUCCESS								0
#define DB_ERROR									5000
#define DB_FAIL_INITIALIZE_PROCESS_ENVIRONMENT	DB_ERROR +1
#define DB_FAIL_FREE_ENVIRONMENT_HANDLE			DB_ERROR +2
#define DB_FAIL_INVALID_DATATYPE					DB_ERROR +3
#define DB_FAIL_CLOSE_SESSION						DB_ERROR +4
#define DB_OCINUMBER_IS_INTEGER					DB_ERROR +5
/*-----------------------------------------------------*/


/*******************************************************/
class CDBException_;
class CVariant_;
class CDBColumnDescriptor_;
class CDBBindingData_;
class CDBRecord_;
class CDatabase_;
class CRecordset_;
class CDBPreStmt_;
/*******************************************************/


/*--------------------------------------------------------------*/
/*                                                              */
/*      class CDBException_                                     */
/*                                                              */
/*--------------------------------------------------------------*/
class CDBException_
{
public:
	CDBException_();
	~CDBException_();

public:
	void BuildErrorString(short nRetCode, OCIEnv *envhp);
	void BuildErrorString(short nRetCode, OCIError *errhp);
	void SetErrorString(short nRetCode, LPCSTR lpszError);
	void Empty();

public:
	int	m_nRetCode;
	BYTE	m_szError[_MAX_ERRMESSAGE];
};

void _DB_ThrowException(short nRetCode, OCIEnv *envhp);
void _DB_ThrowException(short nRetCode, OCIError *errhp);
void _DB_ThrowException(short nRetCode, LPCSTR lpszError);


/*--------------------------------------------------------------*/
/*                                                              */
/*      class CDatabase_                                        */
/*                                                              */
/*--------------------------------------------------------------*/
class CDatabase_
{
	friend class CRecordset_;
public :
	CDatabase_();
	~CDatabase_();

public :
	/* called to establishes a connection to a database data source */
	/* try...catch... */
	int  Open( LPBYTE dbserver, LPBYTE database, 
			LPBYTE username, LPBYTE password,
			BOOL  bReadOnly=FALSE );

	/* called to closes the data source connection */
	/* try...catch... */
	int  Close();

	/* called to executes a sql statement, an sql stored procedure or
	   an sql stored function, with or without sql stored package */
	/* try...catch... */
	int  ExecuteSQLDirect( LPCSTR sql_stmt, 
			CRecordset_ *pResultset=NULL,
			short sType =IS_PROCEDURE | IS_CURSOR );

	/* called to get value */
	int  GetItemValueA(CVariant_ *pItem, LPBYTE pVal, int nType);
	/* try...catch... */
	int  GetItemValue(CVariant_ *pItem, LPBYTE pVal, int nType);

	int GetIntFromOCINumber(OCINumber* pnumber, LPBYTE iVal);
	int GetUIntFromOCINumber(OCINumber* pnumber, LPBYTE pVal);
	int GetRealFromOCINumber(OCINumber* pnumber, LPBYTE fVal);
	int GetGMTTimeFromOCIDate(OCIDate*  ptime, gmt_time* ptm);
	
	int PrepareSQLStmt(LPCSTR pszSQLStmt);
	int BindInParamByName(LPCSTR pszParamName, DWORD &dwContent);
	int BindInParamByName(LPCSTR pszParamName, LPBYTE pszContent);
	int ExecutePreparedSQLStmt();
	int BindOutParamByName(LPCSTR pszParamName, DWORD &dwContent);
	int BindOutParamByName(LPCSTR pszParamName, LPBYTE pszContent);

private :
	int Init();
	int  Cleanup();	
	void ParseSQLStatement();	

private :
	BOOL		m_bReadOnly;	/* not support */
	BOOL		m_bConnected;
	BYTE		m_dbserver[_MAX_DBLENGTH];
	BYTE		m_database[_MAX_DBLENGTH];
	BYTE		m_username[_MAX_DBLENGTH];
	BYTE		m_password[_MAX_DBLENGTH];
	int			m_init;
public :
	OCIEnv      *m_envhp;       /* OCI enviornment handle */
	OCIError    *m_errhp;       /* OCI error handle */
	OCISession  *m_seshp;       /* OCI session handle */
	OCIServer   *m_srvhp;       /* OCI server handle */
	OCISvcCtx   *m_svchp;       /* OCI service context handle */
	OCIStmt     *m_stmhp;       /* OCI sql statement handle */
	OCIStmt     *m_cursor;      /* OCI sql statement handle */

	/* sql statement */
	BYTE		m_sqlstmt[_MAX_SQLLENGTH];
	int			m_iRet;		/* return value from stored function */

	CDBPreStmt_*	m_pDBPreStmt;
	bool 		m_bSQLStmtBind;		//indicate wether the SQL has Binded, and ready to execute
	bool 		m_bSQLStmtPrepared;	//indicate wether the SQL prepared, and ready to be bind
};



/*--------------------------------------------------------------*/
/*                                                              */
/*      class CArray_                                           */
/*                                                              */
/*--------------------------------------------------------------*/
class CArray_
{
public:
	CArray_();
	CArray_(LPVOID* ppHead, int nSize);
	~CArray_();

public:
	BOOL Add(LPVOID lpv, BOOL bCheckDuplicate = FALSE);/* synchronized */
	BOOL InsertAt(int nIndex, LPVOID lpv);  /* synchronized */
	BOOL Remove(LPVOID lpv);                /* synchronized */
	BOOL RemoveAt(int i);                   /* synchronized */
	void RemoveAll();                       /* synchronized */
	CArray_* Clone();                       /* synchronized */

	int GetCount();                         /* synchronized */

	LPVOID operator [](int i);              /* synchronized */
	BOOL SetAt(int nIndex, LPVOID lpv);     /* synchronized */
	int GetIndex(LPVOID lpv);               /* synchronized */
	LPVOID* GetData();

protected:
	BOOL Expand();

protected:
	LPVOID*         m_ppHead;
	int             m_nAlloc;
	int             m_nUsed;
};


/*--------------------------------------------------------------*/
/*                                                              */
/*      class CBinary_                                          */
/*                                                              */
/*--------------------------------------------------------------*/
class CBinary_
{
public:
	CBinary_()
	{
		m_pData = NULL;
		m_dwSize = 0;
	}

	~CBinary_()
	{
		if(m_pData && m_dwSize>0)
		{
			delete [] m_pData;
			m_dwSize = 0;
			m_pData = NULL;
		}
	}

public:
	CBinary_& operator = (CBinary_& other)
	{
		if(other.m_pData && other.m_dwSize>0)
		{
			m_dwSize = other.m_dwSize;
			m_pData = new BYTE[other.m_dwSize];
			if(m_pData)
				memcpy(m_pData, other.m_pData, other.m_dwSize);
		}

		return *this;
	}

	void CopyData(LPBYTE pData, DWORD dwSize)
	{
		if(pData && dwSize>0)
		{
			m_pData = new BYTE[dwSize];
			if(m_pData)
				memcpy(m_pData, pData, dwSize);
		}
	}

public:
	LPBYTE  m_pData;
	DWORD   m_dwSize;
};


/*--------------------------------------------------------------*/
/*                                                              */
/*      class CVariant_                                         */
/*                                                              */
/*--------------------------------------------------------------*/
class CM_OS_EXPORT CVariant_
{
public:
	CVariant_();
	CVariant_(short nType, LPBYTE pData, DWORD dwSize);
	~CVariant_();

public:
	void Clear();
	CVariant_& operator = (CVariant_& other);
	void SetValue(short nType, LPBYTE pData, DWORD dwSize);
	void GetValue(short* pnType, LPBYTE* ppData, DWORD* pdwSize);

public:
	short   m_nType;
	union
	{
		int             m_iVal;         /* SQLT_INT */
		UINT            m_uiVal;        /* SQLT_UIN */
		double          m_fltVal;       /* SQLT_FLT */
		OCIDate         m_time;         /* SQLT_DAT */
		OCINumber       m_number;       /* SQLT_NUM */
		LPSTR           m_pString;      /* SQLT_CHR */
		char            m_char;			/* SQLT_AFC */
		CBinary_*       m_pBinary;      /* SQLT_BIN */
                                        /* SQLT_PDN */
	};
};



/*--------------------------------------------------------------*/
/*                                                              */
/*      class CRecordset_                                       */
/*                                                              */
/*--------------------------------------------------------------*/
class CM_OS_EXPORT CRecordset_
{
	friend class CDatabase_;
public :
	CRecordset_(CDatabase_ *pDatabase);
	~CRecordset_();

public :
	/* called to initialize parameters */
	/* try...catch... */
	int  Initialize();
	/* called to clean up */
	int Destroy();
	int	 CleanupA();
	int  Cleanup();

	/* called to prepare a sql statement */
	/* try...catch... */
	int  PrepareSQL(LPBYTE sql_stmt);

	/* called to create an association between a select-list
	   variable and an input variable by Name or by Position */
	/* try...catch... */
	int  BindByName( LPBYTE placeholder,
			dvoid* valuep, int value_size,
			USHORT datatype,
			BOOL bIsCursorStmt=FALSE );
	/* try...catch... */
	int  BindByPos( UINT position,
			dvoid* valuep, int value_size,
			USHORT datatype,
			BOOL bIsCursorStmt=FALSE );
	
	/* called to create an association between a select-list
	   variable and a output variable by position */
	/* try...catch... */
	int  DefineByPos(UINT position,
			dvoid* valuep, int value_size,
			USHORT datatype,
			BOOL bIsCursorStmt=FALSE);
	
	/* called to execute a sql statement, and get the first record */
	/* try...catch... */
	int  ExecuteSQL(BOOL bDescribeOnly=FALSE);

	/* called to fetch query result */
	/* try...catch... */
	int  FetchNext(BOOL bIsCursorStmt=FALSE);
	
public:
	/* called to create associations between select-list variables 
	   and output buffer, execute sql statement, fill all of query
	   result set into output buffer */
    int  FillResultSet(short sType);

	/* called to get column count */
	int  GetColumnCount();

	/* called to get column name according to index */
	LPSTR GetColumnName(int nColIndex);

	/* called to get column data type according to index */
	ub2  GetColumnType(int nColIndex);

	/* called to get column data size according to index */
	int  GetColumnSize(int nColIndex);

	/* called to get record count */
	int  GetRecordCount();

	/* called to get a record according to index */
	CDBRecord_ *GetRecord(int nRecIndex);

protected:
	/* called to get column descriptors information */
	int  DescribeColumns(int nNumCols, CArray_ &aBindingList,
			short sType ); 

	/* called to create associations between select-list variables
       and outbuffer */
	int  DefineColumns(CArray_ &aBindingList, BOOL bIsCursorStmt);

	/* called to add one column descriptor */
	void AddColumnDescriptor(CDBColumnDescriptor_ *pColDesc);

	/* called to add one record */
	void AddRecord(CDBRecord_ *pRecord);

	void ReleaseArray(CArray_ &aBindingList);

private:
	CDatabase_	*m_pDatabase;
	OCIStmt		*m_stmhp;	/* OCI sql statement handle */
	OCIStmt		*m_cursor;	/* OCI sql statement handle */
    	OCIParam    *m_colhp;	/* OCI parameter handle */

	/* OCI bind handles */
	OCIBind		*m_bndhp[_MAX_PARAMETERS];
	/* OCI define handles */
	OCIDefine	*m_defhp[_MAX_PARAMETERS];

	/* bind indicator pointers, for NULL */
	sb2			bnd_indp[_MAX_PARAMETERS];
	/* define indicator pointers, for NULL */
	sb2			def_indp[_MAX_PARAMETERS];

	/* index avaiable bind handle for use */
    int         n_Bind;         
	/* index avaiable define handle for use */
    int         n_Define;       

	BYTE		m_sqlstmt[_MAX_SQLLENGTH];

	/* array for column descriptors */
	CArray_ 	m_aColDescriptors;

	/* array for records */
	CArray_		m_aRecords;
};


/*--------------------------------------------------------------*/
/*                                                              */
/*      class DBColumnDescriptor_								*/
/*                                                              */
/*--------------------------------------------------------------*/
class CDBColumnDescriptor_
{
public:
	CDBColumnDescriptor_(LPSTR lpszName, ub4 col_name_len, 
			ub2 dataType, int nMaxSize);
	~CDBColumnDescriptor_();

public:
	LPSTR  GetName();			/* get column name */
	ub2  	GetDataType();		/* get column data type */
	int  	GetMaxSize();		/* get column data size */

private:
	TCHAR	m_szName[256];
	ub2		m_nDataType;
	int		m_nMaxSize;
};


/*--------------------------------------------------------------*/
/*                                                              */
/*      class CDBBindingData_                                   */
/*                                                              */
/*--------------------------------------------------------------*/
class CDBBindingData_
{
public:
	CDBBindingData_(ub2 nType, LPVOID pData, int len);
	~CDBBindingData_();

public:
	void Clear();

public:
	BYTE	*m_pDataBuf;		/* data buffer */
	UINT	m_nMaxDataLen;		/* maximum data length */
	UINT	m_nRealDataLen;		/* real data length */
	ub2		m_nType;			/* data type */
};


/*--------------------------------------------------------------*/
/*                                                              */
/*      class DBRecord_											*/
/*                                                              */
/*--------------------------------------------------------------*/
class CM_OS_EXPORT CDBRecord_
{
public:
	CDBRecord_();
	~CDBRecord_();

public:
	int	 GetItemCount();	
    void AddDataItem(CVariant_ *pItem);
	CVariant_ *GetDataItem(int nColIndex);

private:
	CArray_ m_aDataItems;
};

class CM_OS_EXPORT CDBPreStmt_
{
public:
	CDBPreStmt_(CDatabase_* pDB, LPCSTR pszSQLStmt);
	~CDBPreStmt_();
public:
	void BindInParamByName(LPCSTR pszParamName, DWORD &dwContent);	
	void BindInParamByName(LPCSTR pszParamName, LPBYTE pszContent);	
	void BindOutParamByName(LPCSTR pszParamName, DWORD &dwContent);
	void BindOutParamByName(LPCSTR pszParamName, LPBYTE pszContent);
private:
	class CDatabase_* m_pDB;
	LPBYTE m_pszSQLStmt;
#define MAX_CONTENT_LEN	4000
	sb2	m_Indicator[_MAX_PARAMETERS];
	int m_nIndicatorIndex;
};

#endif//__ORACLE__

#endif	/* __DBOCI_H__ */
